package sketch.icse11nier;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import junit.framework.TestCase;

import org.eclipse.jdt.core.dom.BodyDeclaration;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.TypeDeclaration;

import sketch.ast.ASTUtils;
import sketch.instrumenter.SourceCodeInstrumenter;
import sketch.instrumenter.Tracer;
import sketch.instrumenter.TracerFileReader;
import sketch.ounit.ObservationInstantiationVisitor;
import sketch.specs.MethodDumperAfterRemovingUnusedVars;
import sketch.specs.RedundantParenthesesRemover;
import sketch.specs.SketchedMethodDeclaration;
import sketch.util.Globals;

/**
 * A utility method to make life easier
 * */
public class GeneratedTestsOutput extends TestCase {
	
	public static String tmp_dir_instrumentation = "./instrument/";
	
	public static void generateTestsAndWriteToFile(List<SketchedMethodDeclaration> sketchedMethods, CompilationUnit unit,
			String outputFileDir, String packageName, String outputClassName) {
		//the output generated method
		List<MethodDeclaration> generatedMethod = new LinkedList<MethodDeclaration>();
		for(SketchedMethodDeclaration sketchedMethod : sketchedMethods) {
			List<MethodDeclaration> expanded = sketchedMethod.getMethodsAfterExpansion();
			System.out.println(expanded.size());
			for(MethodDeclaration expand : expanded) {
				MethodDumperAfterRemovingUnusedVars dumper = new MethodDumperAfterRemovingUnusedVars(expand);
				dumper.dumpMethodWithUnusedVarRemoved();
				ASTUtils.makeMethodPublic(expand);
				ASTUtils.addOUnitAnnotation(expand);
			}
			generatedMethod.addAll(expanded);
		}
		
		
		assertEquals(1, unit.types().size());
		
		//get the type, and add methods
		TypeDeclaration typeDeclaration = (TypeDeclaration)unit.types().get(0);
		
		//delete the test sketches, and add more
		List<BodyDeclaration> bodyDeclarations = typeDeclaration.bodyDeclarations();
		bodyDeclarations.addAll(generatedMethod);
		
		//set the name
		ASTUtils.setTypeName(typeDeclaration, outputClassName);
		
		//remove all test sketches after expansion
		ASTUtils.removeTestSketches(unit);
		ASTUtils.setPackageDeclaration(unit, packageName);
		
		//try to remove redundant parenthesis
		RedundantParenthesesRemover<CompilationUnit> remover = new RedundantParenthesesRemover<CompilationUnit>(unit);
		try  {
		   unit = remover.getResult();
		} catch (RuntimeException e) {
			System.err.println("Error occurs in removing redundant { }, simply ignore that.");
		}
		
		String outputFileAbsolutePath = outputFileDir + Globals.fileSep + outputClassName + ".java";
		
		
		CompilationUnit copy_for_instrumentation = ASTUtils.deepClone(unit);
		SourceCodeInstrumenter instrumenter = new SourceCodeInstrumenter(copy_for_instrumentation);
		instrumenter.doInstrumentation();
		
		CompilationUnit before_instrumentation_unit = ASTUtils.deepClone(unit);
		boolean hasInstrumented = instrumenter.hasInstrumentedTheCode();
		if(hasInstrumented) {
			String original_file_path = tmp_dir_instrumentation + Globals.fileSep + outputClassName + ".java";
			ASTUtils.outputUnit(unit, original_file_path);
			unit = copy_for_instrumentation;
		}
		
		//output the instrumented
		ASTUtils.outputUnit(unit, outputFileAbsolutePath);
		
		//to execute the program and find the results
		if(hasInstrumented) {
			Class<?> testClass = null;
			try {
				String clazzName = packageName + "." + outputClassName;
				testClass = Class.forName(clazzName);
			} catch (ClassNotFoundException e) {
				throw new RuntimeException(" class: " + packageName + "." + outputClassName + " does not exist!");
			}
			//clean the file
			
			Tracer.clearFile();
			
			//run the tests
			junit.textui.TestRunner.run(testClass);
			
			System.out.println("Finish executing JUnit tests, and start rewriting!");
			
			TracerFileReader reader = new TracerFileReader(Tracer.saveFile);
			Map<String, Map<Integer, String>> parsingResults = reader.parse();
			
			System.out.println(parsingResults);
			
			//replace the oracle with concrete values
			ObservationInstantiationVisitor visitor = new ObservationInstantiationVisitor(before_instrumentation_unit, parsingResults); 
			before_instrumentation_unit.accept(visitor);
			CompilationUnit unit_after_instantiate_oracle = before_instrumentation_unit;
			
			//replace it
			ASTUtils.outputUnit(unit_after_instantiate_oracle, outputFileAbsolutePath);
			System.out.println(unit_after_instantiate_oracle);
		}
		
	}
	
}